// Copyright (c) 2009 Mike Haight, Andy Kipp
//
// Permission is hereby granted, free of charge, to any person
// obtaining a copy of this software and associated documentation
// files (the "Software"), to deal in the Software without
// restriction, including without limitation the rights to use,
// copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following
// conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
// OTHER DEALINGS IN THE SOFTWARE.

#pragma once

#include "stdafx.h"

namespace HandBanana { namespace Utilities { namespace Xml {

    /// <summary>
    /// Convert a wide-string to an interger
    /// </summary>
    int wstrparseint(const std::wstring& argString);

    /// <summary>
    /// Convert a wide-string to a long
    /// </summary>
    long wstrparselong(const std::wstring& argString);

    /// <summary>
    /// 
    /// </summary>
    enum XmlNodeType
    {
        Unknown,
        Element,
        Text,
        Comment,
        DocumentType,
        Declaration,
        Document
    };

    class XmlReader;
    class XmlWriter;

    /// <summary>
    /// 
    /// </summary>
    class XmlException : public std::exception
    {
    // Constructor
    public:
        /// <summary>
        /// Create a new instance of the XmlException class
        /// </summary>
        XmlException(const std::wstring& argMessage);


    // Private Data Members
    private:
        std::wstring mMessage;
    };

    /// <summary>
    /// The XmlNode class represents an XML node in an XmlDocument
    /// </summary>
    class XmlNode
    {
    // Constructor
    public:
        /// <summary>
        /// Create an instance of the XmlNode class
        /// </summary>
        XmlNode(XmlNodeType argType);


    // Destructor
    public:
        /// <Summary>
        /// Destory instance of the XmlNode class
        /// </summary>
        ~XmlNode(void);


    // Public Methods
    public:
        /// <summary>
        /// Get the XmlNodeType of the XmlNode
        /// </summary>
        XmlNodeType GetType(void) const;

        /// <summary>
        /// Get the Value of the XmlNode
        /// </summary>
        virtual std::wstring GetValue(void) const;

        /// <summary>
        /// Set the Value of the XmlNode
        /// </summary>
        virtual void SetValue(const std::wstring& argValue);

        /// <summary>
        /// Get the parent XmlNode
        /// </summary>
        XmlNode* GetParent(void) const;

        /// <summary>
        /// Append a child XmlNode
        /// </summary>
        virtual void AppendChild(XmlNode* argNode);

        /// <summary>
        /// Remove a child XmlNode
        /// </summary>
        virtual void RemoveChild(XmlNode* argNode);

        /// <summary>
        /// Return a value indicating of a XmlNode has children
        /// </summary>
        bool HasChildren(void) const;

        /// <summary>
        /// Get the first child XmlNode
        /// </summary>
        XmlNode* GetFirstChild(void);

        /// <summary>
        /// Get the last child XmlNode
        /// </summary>
        XmlNode* GetLastChild(void);

        /// <summary>
        /// Get the next child XmlNode
        /// </summary>
        XmlNode* GetNextChild(void);

        /// <summary>
        /// Get the previous child XmlNode
        /// </summary>
        XmlNode* GetPrevChild(void);

        /// <summary>
        /// Get the next sibling XmlNode
        /// </summary>
        XmlNode* GetNextSibling(void);

        /// <summary>
        /// Get the previous sibling XmlNode
        /// </summary>
        XmlNode* GetPrevSibling(void);


    // Private Data Members
    private:
        XmlNodeType                         mType;
        std::wstring                        mValue;
        std::vector<XmlNode*>               mChildren;
        std::vector<XmlNode*>::iterator     mChildrenItr;
        XmlNode*                            mParent;


    };

    /// <summary>
    /// The XmlAttribute class represents an XML attribute in an XmlDocument
    /// </summary>
    class XmlAttribute
    {
    // Constructor
    public:
        /// <summary>
        /// Create an instance of the XmlAttribute class
        /// </summary>
        XmlAttribute(const std::wstring& argName, const std::wstring& argValue);


    // Destructor
    public:
        /// <summary>
        /// Destory instance of the XmlAttribute class
        /// </summary>
        ~XmlAttribute(void);


    // Public Methods
    public:
        /// <summary>
        /// Get the Name of the XmlAttribute
        /// </summary>
        std::wstring GetName(void) const;

        /// <summary>
        /// Get the Value of the XmlAttribute
        /// </summary>
        std::wstring GetValue(void) const;

        /// <summary>
        /// Set the Name of the XmlAttribute
        /// </summary>
        void SetName(const std::wstring& argName);

        /// <summary>
        /// Set the Value of the XmlAttribute
        /// </summary>
        void SetValue(const std::wstring& argValue);


    // Private Data Members
    private:
        std::wstring                    mName;
        std::wstring                    mValue;


    };

    /// <summary>
    /// The XmlElement class represents an XML element in an XmlDocument
    /// </summary>
    class XmlElement : public XmlNode
    {
    // Constructor
    public:
        /// <summary>
        /// Create an instance of the XmlElement class
        /// </summary>
        XmlElement(const std::wstring& argName);


    // Destructor
    public:
        /// <summary>
        // Destory instance of the XmlElement class
        /// </summary>
        ~XmlElement(void);


    // Public Methods
    public:
        /// <summary>
        /// Get the Name of the XmlElement
        /// </summary>
        std::wstring GetName(void) const;

        /// <summary>
        /// Set the Name of the XmlElement
        /// </summary>
        void SetName(const std::wstring& argName);

        /// <summary>
        /// Get the Value of the XmlElement
        /// </summary>
        virtual std::wstring GetValue(void) const;

        /// <summary>
        /// Set the Value of the XmlElement
        /// </summary>
        virtual void SetValue(const std::wstring& argValue);

        /// <summary>
        /// Return a value indicating if the XmlElement has any XmlAttributes
        /// </summary>
        bool HasAttributes(void) const;

        /// <summary>
        /// Add an XmlAttribute to an XmlElement
        /// </summary>
        void AddAttribute(XmlAttribute* argAttribute);

        /// <summary>
        /// Remove an XmlAttribute from an XmlElement
        /// </summary>
        void RemoveAttribute(XmlAttribute* argAttribute);

        /// <summary>
        /// Get the first XmlAttribute in the XmlElement
        /// </summary>
        XmlAttribute* GetFirstAttribute();

        /// <summary>
        /// Get the last XmlAttribute in the XmlElement
        /// </summary>
        XmlAttribute* GetLastAttribute();

        /// <summary>
        /// Get the next XmlAttribute in the XmlElement
        /// </summary>
        XmlAttribute* GetNextAttribute();

        /// <summary>
        /// Get the previous XmlAttribute in the XmlElement
        /// </summary>
        XmlAttribute* GetPrevAttribute();

        /// <summary>
        /// Get an XmlAttributes from an XmlElement
        /// </summary>
        XmlAttribute* GetAttribute(const std::wstring& argName);


    // Private Data Members
    private:
        std::wstring                                            mName;
        std::map<const std::wstring, XmlAttribute*>             mAttributes;
        std::map<const std::wstring, XmlAttribute*>::iterator   mAttributesItr;

    };

    /// <summary>
    /// The XmlComment class represents an block of text in an XmlDocument
    /// </summary>
    class XmlText : public XmlNode
    {
    // Constructor
    public:
        /// <summary>
        /// Create an instance of the XmlText class
        /// </summary>
        XmlText(void);


    // Destructor
    public:
        /// <summary>
        /// Destory instance of the XmlText class
        /// </summary>
        ~XmlText(void);


    };

    /// <summary>
    /// The XmlComment class represents an XML comment in an XmlDocument
    /// </summary>
    class XmlComment : public XmlNode
    {
    // Constructor
    public:
        /// <summary>
        /// Create an instance of the XmlComment class
        /// </summary>
        XmlComment(void);


    // Destructor
    public:
        /// <summary>
        /// Destory instance of the XmlComment class
        /// </summary>
        ~XmlComment(void);


    };

    /// <summary>
    /// 
    /// </summary>
    class XmlDocument : public XmlNode
    {
    // Constructor
    public:
        /// <summary>
        /// Create a new instance of the XmlDocument class
        /// </summary>
        XmlDocument();


    // Destructor
    public:
        /// <summary>
        /// Destory instance of the XmlDocument class
        /// </summary>
        ~XmlDocument(void);


    // Public Methods
    public:
        /// <summary>
        /// Override the XmlNode::AppendChild method
        /// </summary>
        virtual void AppendChild(XmlNode* argNode);

        /// <summary>
        /// Override the XmlNode::RemoveChild method
        /// </summary>
        virtual void RemoveChild(XmlNode* argNode);

        /// <summary>
        /// Get Document XmlElement
        /// </summary>
        XmlElement* GetDocumentElement(void);

        /// <summary>
        /// Write the XmlDocument to a file
        /// </summary>
        void Write(const std::wstring &argFileName);

        /// <summary>
        /// Write the XmlDocument to an output stream
        /// </summary>
        void Write(std::wostream &argStream);

        /// <summary>
        /// Write the XmlDocument to XmlWriter
        /// </summary>
        void Write(const XmlWriter &argWriter);

        /// <summary>
        /// Load an XmlDocument from a file
        /// </summary>
        void Load(const std::wstring &argFileName);

        /// <summary>
        /// Load an XmlDocument from a input stream
        /// </summary>
        void Load(std::wistream &argStream);

        /// <summary>
        /// Load an XmlDocument from a XmlReader
        /// </summary>
        void Load(const XmlReader &argReader);

    };

    /// <summary>
    /// 
    /// </summary>
    class XmlReader
    {
    // Constructor
    public:
        /// <summary>
        /// Create a new instance of the XmlReader Class
        /// </summary>
        XmlReader(std::wistream* argStream);


    // Public Methods
    public:
        /// <summary>
        /// Parse the Xml into a new XmlDocument
        /// </summary>
        void ParseDocument(XmlDocument* argDocument) const;


    // Private Data Members
    private:
        /// <summary>
        /// Return a value indicating if the pointer is an Xml Declaration
        /// </summary>
        bool IsDeclaration(wchar_t * p) const;

        /// <summary>
        /// Return a value indicating if the pointer is an Xml Comment
        /// </summary>
        bool IsComment(wchar_t * p) const;

        /// <summary>
        /// Return a value indicating if the pointer is an Xml Element
        /// </summary>
        bool IsElement(wchar_t* p) const;

        /// <summary>
        /// Return a value indicating if the pointer is an Xml Attribute
        /// </summary>
        bool IsEndElement(wchar_t* p) const;

        /// <summary>
        /// Return a value indicating if the pointer is an Xml Text
        /// </summary>
        bool IsText(wchar_t* p) const;

        /// <summary>
        /// Parse an Xml Decleration
        /// </summary>
        wchar_t* ParseDeclaration(wchar_t* p, XmlNode* parent) const;

        /// <summary>
        /// Parse an Xml Comment
        /// </summary>
        wchar_t* ParseComment(wchar_t* p, XmlNode* parent) const;

        /// <summary>
        /// Parse an Xml Element
        /// </summary>
        wchar_t* ParseElement(wchar_t* p, XmlNode* parent) const;

        /// <summary>
        /// Parse Xml Text
        /// </summary>
        wchar_t* ParseText(wchar_t* p, XmlNode* parent) const;

        /// <summary>
        /// Parse an End Xml Element
        /// </summary>
        wchar_t* ParseEndElement(wchar_t* p, XmlElement* startElement) const;

        /// <summary>
        /// Parse White Space
        /// </summary>
        wchar_t* ParseWhiteSpace(wchar_t* p) const;

        /// <summary>
        /// Read attributes
        /// </summary>
        wchar_t* ReadAttributes(wchar_t* p, XmlElement* element) const;

        /// <summary>
        /// Read a name of a tag or attribute
        /// </summary>
        wchar_t* ReadName(wchar_t* p, std::wstring* name) const;

        /// <summary>
        /// Return a value indicating if the strings match
        /// </summary>
        bool StringEquals(wchar_t* p, const wchar_t* match) const;

        /// <summary>
        /// Return a value indicating if the character is a whitespace character
        /// </summary>
        bool IsWhiteSpace(const wchar_t c) const;

        /// <summary>
        /// Return a value indicating if the character is a alpha numeric
        /// </summary>
        bool IsAlphaNumeric(const wchar_t c) const;

        /// <summary>
        /// Return a value indicating if the character is a letter
        /// </summary>
        bool IsAlpha(const wchar_t c) const;


    // Private Data Members
    private:
         std::wistream*                stream;


    };

    /// <summary>
    ///
    /// </summary>
    class XmlWriter
    {
    // Constructor
    public:
        /// <summary>
        /// Create a new instance of the XmlWriter class
        /// </summary>
        XmlWriter(std::wostream* argStream);


    // Public Methods
    public:
        /// <summary>
        /// Write an XmlDocument
        /// </summary>
        void WriteDocument(XmlDocument* argDocument) const;

        /// <summary>
        /// Write an XmlElement
        /// </summary>
        void WriteElement(XmlElement* element) const;

        /// <summary>
        /// Write an XmlAttribute
        /// </summary>
        void WriteAttribute(XmlAttribute* attr) const;

        /// <summary>
        /// Write an XmlText
        /// </summary>
        void WriteText(XmlText* text) const;


    // Private Data Members
    private:
        std::wostream*                 stream;


    };

    /// <summary>
    ///
    /// </summary>
    interface IXmlSerializable
    {
    // Public Methods
    public:
        /// <summary>
        ///
        /// </summary>
        virtual void ReadXml(const XmlReader& argParser) = NULL;

        /// <summary>
        ///
        /// </summary>
        virtual void WriteXml(const XmlWriter& argWriter) = NULL;


    };

}}}